Перейти к основному содержимому

Указатели на функцию

Что такое указатель на функцию

Указатель на функцию — это переменная, которая хранит адрес функции в памяти. Через такой указатель можно вызывать функцию.

int add(int a, int b) {
return a + b;
}

int (*funcPtr)(int, int) = add; // Указатель на функцию add
int result = funcPtr(5, 3); // Вызов через указатель

Объявление указателей на функции

Синтаксис объявления

тип_возврата (*имя_указателя)(параметры);

Базовые примеры

#include <stdio.h>

// Простые функции
int multiply(int x, int y) {
return x * y;
}

void printMessage(void) {
printf("Сообщение из функции\n");
}

float divide(float a, float b) {
return b != 0 ? a / b : 0;
}

int main() {
// Объявление указателей на функции
int (*mathOp)(int, int) = multiply; // Указатель на функцию с двумя int
void (*printer)(void) = printMessage; // Указатель на void функцию
float (*divider)(float, float) = divide; // Указатель на функцию с float

// Вызовы через указатели
printf("5 × 7 = %d\n", mathOp(5, 7));
printer();
printf("10.0 / 3.0 = %.2f\n", divider(10.0, 3.0));

return 0;
}

Массивы указателей на функции

Таблица функций

#include <stdio.h>

// Функции для калькулятора
int add(int a, int b) { return a + b; }
int subtract(int a, int b) { return a - b; }
int multiply(int a, int b) { return a * b; }
int divide(int a, int b) { return b != 0 ? a / b : 0; }

int main() {
// Массив указателей на функции
int (*operations[4])(int, int) = {add, subtract, multiply, divide};
char *operationNames[4] = {"Сложение", "Вычитание", "Умножение", "Деление"};
char symbols[4] = {'+', '-', '×', '÷'};

int num1 = 12, num2 = 4;

printf("=== КАЛЬКУЛЯТОР ===\n");
printf("Числа: %d и %d\n\n", num1, num2);

for (int i = 0; i < 4; i++) {
int result = operations[i](num1, num2); // Вызов через массив указателей
printf("%s: %d %c %d = %d\n",
operationNames[i], num1, symbols[i], num2, result);
}

return 0;
}

Функции как параметры других функций

Передача функций в качестве аргументов

#include <stdio.h>

// Функции обработки массива
void doubleElements(int arr[], int size) {
for (int i = 0; i < size; i++) {
arr[i] *= 2;
}
}

void squareElements(int arr[], int size) {
for (int i = 0; i < size; i++) {
arr[i] *= arr[i];
}
}

void incrementElements(int arr[], int size) {
for (int i = 0; i < size; i++) {
arr[i] += 1;
}
}

// Функция, принимающая другую функцию как параметр
void processArray(int arr[], int size, void (*processor)(int[], int), char *operation) {
printf("До %s: ", operation);
for (int i = 0; i < size; i++) printf("%d ", arr[i]);
printf("\n");

processor(arr, size); // Вызываем переданную функцию

printf("После %s: ", operation);
for (int i = 0; i < size; i++) printf("%d ", arr[i]);
printf("\n\n");
}

int main() {
int numbers[5] = {1, 2, 3, 4, 5};

printf("Демонстрация обработки массива:\n");

// Создаем копии для разных операций
int copy1[5] = {1, 2, 3, 4, 5};
int copy2[5] = {1, 2, 3, 4, 5};
int copy3[5] = {1, 2, 3, 4, 5};

processArray(copy1, 5, doubleElements, "удвоения");
processArray(copy2, 5, squareElements, "возведения в квадрат");
processArray(copy3, 5, incrementElements, "инкремента");

return 0;
}

Сложные указатели на функции

Функции возвращающие указатели на функции

#include <stdio.h>

// Математические операции
int add(int a, int b) { return a + b; }
int multiply(int a, int b) { return a * b; }

// Функция, возвращающая указатель на нужную операцию
int (*getOperation(char op))(int, int) {
switch (op) {
case '+': return add;
case '*': return multiply;
default: return NULL;
}
}

int main() {
char operation = '+';
int num1 = 8, num2 = 5;

// Получаем указатель на нужную функцию
int (*selectedOp)(int, int) = getOperation(operation);

if (selectedOp != NULL) {
int result = selectedOp(num1, num2);
printf("%d %c %d = %d\n", num1, operation, num2, result);
} else {
printf("Неизвестная операция\n");
}

return 0;
}

Практические применения

Система обработки событий

#include <stdio.h>

// Типы событий
typedef enum {
EVENT_CLICK,
EVENT_KEYPRESS,
EVENT_TIMER,
EVENT_ERROR
} EventType;

// Обработчики событий
void handleClick(int data) {
printf("🖱️ Обработка клика: данные = %d\n", data);
}

void handleKeypress(int keyCode) {
printf("⌨️ Нажата клавиша: код = %d\n", keyCode);
}

void handleTimer(int seconds) {
printf("⏰ Таймер сработал: %d секунд\n", seconds);
}

void handleError(int errorCode) {
printf("🚨 Ошибка: код = %d\n", errorCode);
}

// Диспетчер событий
void processEvent(EventType eventType, int data) {
// Таблица обработчиков
void (*handlers[4])(int) = {
handleClick,
handleKeypress,
handleTimer,
handleError
};

char *eventNames[4] = {"CLICK", "KEYPRESS", "TIMER", "ERROR"};

if (eventType >= 0 && eventType < 4) {
printf("Событие: %s\n", eventNames[eventType]);
handlers[eventType](data); // Вызываем соответствующий обработчик
}
}

int main() {
printf("Система обработки событий:\n");

processEvent(EVENT_CLICK, 100);
processEvent(EVENT_KEYPRESS, 65); // Код клавиши 'A'
processEvent(EVENT_TIMER, 30);
processEvent(EVENT_ERROR, 404);

return 0;
}

Указатели на функции как параметры

Передача алгоритмов в функции

#include <stdio.h>

// Различные способы обработки данных
int sumOperation(int a, int b) { return a + b; }
int maxOperation(int a, int b) { return a > b ? a : b; }
int minOperation(int a, int b) { return a < b ? a : b; }

// Функция, применяющая операцию к массиву
int reduceArray(int arr[], int size, int (*operation)(int, int)) {
if (size <= 0) return 0;

int result = arr[0];

for (int i = 1; i < size; i++) {
result = operation(result, arr[i]); // Применяем переданную операцию
}

return result;
}

int main() {
int data[6] = {15, 42, 8, 73, 29, 56};

printf("Массив: ");
for (int i = 0; i < 6; i++) printf("%d ", data[i]);
printf("\n");

// Применяем разные операции к тому же массиву
int sum = reduceArray(data, 6, sumOperation);
int maximum = reduceArray(data, 6, maxOperation);
int minimum = reduceArray(data, 6, minOperation);

printf("Сумма элементов: %d\n", sum);
printf("Максимальный элемент: %d\n", maximum);
printf("Минимальный элемент: %d\n", minimum);

return 0;
}

Практические применения

Система меню с обработчиками

#include <stdio.h>

// Обработчики пунктов меню
void newFile() {
printf("📄 Создание нового файла\n");
}

void openFile() {
printf("📂 Открытие существующего файла\n");
}

void saveFile() {
printf("💾 Сохранение текущего файла\n");
}

void showSettings() {
printf("⚙️ Открытие настроек программы\n");
}

void exitProgram() {
printf("👋 Выход из программы\n");
}

// Структура пункта меню
struct MenuItem {
char *title;
void (*handler)(void);
};

int main() {
// Массив пунктов меню с обработчиками
struct MenuItem menu[5] = {
{"Новый файл", newFile},
{"Открыть файл", openFile},
{"Сохранить", saveFile},
{"Настройки", showSettings},
{"Выход", exitProgram}
};

int choice = 2; // Выбираем пункт 2

printf("=== ГЛАВНОЕ МЕНЮ ===\n");
for (int i = 0; i < 5; i++) {
printf("%d. %s\n", i + 1, menu[i].title);
}

printf("\nВы выбрали пункт %d:\n", choice);

if (choice >= 1 && choice <= 5) {
menu[choice - 1].handler(); // Вызываем обработчик
} else {
printf("Неверный выбор\n");
}

return 0;
}

Функции высшего порядка

Применение функции к каждому элементу

#include <stdio.h>

// Функции преобразования
int doubleValue(int x) { return x * 2; }
int squareValue(int x) { return x * x; }
int incrementValue(int x) { return x + 1; }

// Функция высшего порядка — применяет переданную функцию к каждому элементу
void mapArray(int source[], int target[], int size, int (*transform)(int)) {
for (int i = 0; i < size; i++) {
target[i] = transform(source[i]);
}
}

// Функция фильтрации с предикатом
int filterArray(int source[], int target[], int size, int (*predicate)(int)) {
int targetIndex = 0;

for (int i = 0; i < size; i++) {
if (predicate(source[i])) {
target[targetIndex] = source[i];
targetIndex++;
}
}

return targetIndex; // Количество отфильтрованных элементов
}

// Предикаты для фильтрации
int isEven(int x) { return x % 2 == 0; }
int isPositive(int x) { return x > 0; }
int isLarge(int x) { return x >= 50; }

int main() {
int original[8] = {-5, 12, -3, 67, 24, -8, 45, 78};
int transformed[8];
int filtered[8];

printf("Исходный массив: ");
for (int i = 0; i < 8; i++) printf("%d ", original[i]);
printf("\n");

// Применяем преобразование
mapArray(original, transformed, 8, squareValue);
printf("Квадраты: ");
for (int i = 0; i < 8; i++) printf("%d ", transformed[i]);
printf("\n");

// Фильтруем положительные числа
int positiveCount = filterArray(original, filtered, 8, isPositive);
printf("Положительные (%d): ", positiveCount);
for (int i = 0; i < positiveCount; i++) printf("%d ", filtered[i]);
printf("\n");

// Фильтруем четные числа
int evenCount = filterArray(original, filtered, 8, isEven);
printf("Четные (%d): ", evenCount);
for (int i = 0; i < evenCount; i++) printf("%d ", filtered[i]);
printf("\n");

return 0;
}

Типичные ошибки

Частые проблемы
// ❌ Неправильный синтаксис объявления
int *funcPtr(int, int); // Это функция, возвращающая int*!
// Правильно:
int (*funcPtr)(int, int); // Это указатель на функцию

// ❌ Вызов без разыменования (в старых компиляторах)
int (*ptr)(int, int) = add;
int result = ptr(5, 3); // Современные компиляторы позволяют
int result2 = (*ptr)(5, 3); // Явное разыменование (безопаснее)

// ❌ Неправильное присваивание
int (*mathPtr)(int, int);
mathPtr = add(); // Ошибка! Вызываем функцию вместо получения адреса
// Правильно:
mathPtr = add; // Присваиваем адрес функции

// ❌ Несоответствие сигнатуры
float divide(float a, float b) { return a/b; }
int (*wrongPtr)(int, int) = divide; // Типы не совпадают!
Полезные советы
  • Используйте typedef для сложных типов указателей на функции
  • Проверяйте указатели на NULL перед вызовом
  • Группируйте связанные функции в массивы указателей
  • Документируйте назначение функций-параметров
Преимущества указателей на функции
  • Гибкость — можно менять поведение программы во время выполнения
  • Модульность — разделение логики на независимые функции
  • Расширяемость — легко добавлять новые операции
  • Обратные вызовы — уведомления о событиях

Указатели на функции позволяют создавать гибкие и расширяемые программы с изменяемым поведением.